const EventEmitter = require("events").EventEmitter;
const http = require("http");
const Stream = require("stream");
const context = require("./context");
const request = require("./request");
const response = require("./response");

class Application extends EventEmitter {
  constructor() {
    super();
    this.context = Object.create(context);
    this.request = Object.create(request);
    this.response = Object.create(response);
    this.middlewares = [];
  }
  use(fn) {
    // this.callback = fn;
    this.middlewares.push(fn);
  }
  createContext(req, res) {
    // 每次请求都创建全新的上下文
    let context = Object.create(this.context);
    let request = Object.create(this.request);
    let response = Object.create(this.response);
    context.request = request;
    context.response = response;

    context.request.req = context.req = req;
    context.response.req = context.res = res;
    return context;
  }
  compose(ctx) {
    // 基本递归实现
    /* const dispatch = (i) => {
      if (i === this.middlewares.length) return Promise.resolve();
      let middleware = this.middlewares[i];
      return Promise.resolve(middleware(ctx, () => dispatch(i + 1)));
    }; 
    return dispatch(0); */
    // redux使用reduce实现compose
    /* return this.middlewares.reduce((prev, current) => (...args) => {
      return Promise.resolve(prev(() => current(...args)));
    })(() => {}); */
    // 使用reduce实现compose
    return this.middlewares.reduce((prev, current) => (...args) => {
      return Promise.resolve(prev(ctx, () => current(...args)));
    })(ctx, () => {});
  }
  handleRequest(req, res) {
    let ctx = this.createContext(req, res);
    if (req.url === "/favicon.ico") return;
    // this.callback(ctx);
    this.compose(ctx).then(() => {
      let body = ctx.body;
      if (typeof body === "string" || Buffer.isBuffer(body)) {
        res.end(body);
      } else if (body instanceof Stream) {
        // 如果返回的是流，则进行下载
        res.setHeader(
          "Content-Disposition",
          `attachment; filename=${encodeURIComponent("download")}`
        );
        body.pipe(res);
      } else if (typeof body === "object") {
        res.end(JSON.stringify(body));
      }
      // res.end(ctx.body);
    });
  }
  listen(...args) {
    let server = http.createServer(this.handleRequest.bind(this));
    server.listen(...args);
  }
}

module.exports = Application;
